
/*    This file is part of the managarm operating system.
 *   Copyright (C) 2007, 2008, 2009  Alexander van der Grinten
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

package org.managarm.jmanaintlib.machine;

import java.util.Iterator;
import java.util.HashMap;
import org.managarm.jmanavalib.core.VaClass;
import org.managarm.jmanavalib.core.VaClsInfo;
import org.managarm.jmanavalib.core.VaFldInfo;
import org.managarm.jmanavalib.core.VaMthInfo;
import org.managarm.jmanavalib.core.VaProcedure;
import org.managarm.jmanavalib.utils.VaFldCompWrapper;
import org.managarm.jmanavalib.utils.VaImage;

public final class VmMachine {
	private VaImage image;
	
	// map that stores code information structures
	private HashMap<VaMthInfo, VmCode> codeMap
		= new HashMap<VaMthInfo, VmCode>();
	
	// map that store our static fields
	private HashMap<VaFldCompWrapper, VmCell> statics
		= new HashMap<VaFldCompWrapper, VmCell>();
	
	public long objectId = 1;
	
	public VmMachine(VaImage image) {
		this.image = image;
		
		// create code information structures
		Iterator<VaMthInfo> iter = image.methodIterator();
		while(iter.hasNext()) {
			final VaMthInfo method = iter.next();
			final VaProcedure procedure = image.getMethod(method);
			codeMap.put(method,	new VmVirtualCode(this, method, procedure));
		}
	}
	
	public void setupCode(VmCode codeobject) {
		codeMap.put(codeobject.getMthInfo(), codeobject);
	}
	
	// returns the value of a static field
	public VmCell getStatic(VaFldInfo field) {
		final VmCell cell = statics.get(new VaFldCompWrapper(field));
		if(cell == null) {
			final VmCell newcell = VmUtils.createCell(field.datatype);
			statics.put(new VaFldCompWrapper(field), newcell);
			return newcell;
		}
		return cell;
	}
	
	// stores a value into a static field
	public VmCell putStatic(VaFldInfo field, VmCell cell) {
		return statics.put(new VaFldCompWrapper(field), cell);
	}
	
	// returns the code information for the specified method
	public VmCode getCode(VaMthInfo mthinfo) {
		return codeMap.get(mthinfo);
	}
	
	public VaClass getCls(VaClsInfo info) {
		return image.getClass(info);
	}
	
	// must be called before any methods are invoked
	public void init() throws VmError {
		Iterator<VaClsInfo> iter = image.classIterator();
		while(iter.hasNext())
			runXInit(image.getClass(iter.next()));
	}
	
	// must be called after the task has run
	public void finish() throws VmError {
		Iterator<VaClsInfo> iter = image.classIterator();
		while(iter.hasNext())
			runXFinish(image.getClass(iter.next()));
	}
	
	// returns the code information of a virtual method
	public final VmCode findMethod(VaMthInfo mthinfo, VaClsInfo clsinfo)
			throws VmError {
		if(clsinfo == null)
			throw new VmError("Cannot find method " + mthinfo.ident);
		
		final VaClass clsbody = getCls(clsinfo);
		for(int i = 0; i < clsbody.getMethodCnt(); i++) {
			final VaMthInfo candidate = clsbody.getMethod(i);
			if(candidate.compatible(mthinfo))
				return getCode(candidate);
		}
		
		// try to find the method in the parent class
		return findMethod(mthinfo, clsbody.parentInfo);
	}
	
	private void runXInit(VaClass cls) throws VmError {
		for(int i = 0; i < cls.getMethodCnt(); i++) {
			// get the current field
			VaMthInfo method = cls.getMethod(i);
			
			// add the static fields to the map
			if(method.ident.equals("_xinit"))
				codeMap.get(method).invoke();
		}
	}
	
	private void runXFinish(VaClass cls) throws VmError {
		for(int i = 0; i < cls.getMethodCnt(); i++) {
			// get the current field
			VaMthInfo method = cls.getMethod(i);
			
			// add the static fields to the map
			if(method.ident.equals("_xfinish"))
				codeMap.get(method).invoke();
		}
	}
}
